﻿using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace System
{
    /// <summary>
    /// 모든 클래스들의 확장입니다.
    /// </summary>
    public static class ExtendClass
    {
        #region Extend Method

        /// <summary>
        /// 동일한 인스턴스를 생성합니다.
        /// Serializable 가능한 클래스만 가능하며,
        /// Serializable 가능하지 않으면 null을 반환합니다.
        /// </summary>
        /// <typeparam name="TSource">생성할 인스턴스의 형식입니다.</typeparam>
        /// <param name="target">호출한 대상 인스턴스입니다.</param>
        /// <returns>호출한 대상 인스턴스와 동일한 인스턴스를 반환합니다.</returns>
        public static TSource Make<TSource>(this TSource target) where TSource : class
        {
            if((target is ISerializable) || (Attribute.IsDefined(typeof(TSource), typeof(SerializableAttribute))))
            {
                using(var stream = new MemoryStream())
                {
                    var formatter = new BinaryFormatter();
                    formatter.Serialize(stream, target);
                    stream.Position = 0;
                    return (formatter.Deserialize(stream) as TSource);
                }
            }
            return null;
        }

        /// <summary>
        /// 16진수로 구성된 문자열을 byte 형식으로 변환합니다.
        /// </summary>
        /// <param name="target">호출한 대상 인스턴스입니다.</param>
        /// <returns>변환된 byte[] 형식의 데이터를 반환합니다.</returns>
        public static byte[] HexStringToByteArray(this string target)
        {
            StringBuilder builder = new StringBuilder();
            string[] byteStrings = target.Split(' ', '\t');
            foreach(string s in byteStrings)
            {
                builder.Append(s.Length % 2 == 1 ? String.Format("0{0}", s) : s);
            }
            target = builder.ToString();
            return Enumerable.Range(0, target.Length)
                             .Where<int>(n => n % 2 == 0)
                             .Select<int, byte>(n => Byte.Parse(target.Substring(n, 2), NumberStyles.HexNumber))
                             .ToArray<byte>();
        }

        /// <summary>
        /// byte[] 형식의 데이터를 16진수 문자열로 변환합니다.
        /// </summary>
        /// <param name="target">호출한 대상 인스턴스입니다.</param>
        /// <param name="separator">byte단위로 구분할 구분자입니다.</param>
        /// <returns>변환된 system.String 형식의 데이터를 반환합니다.</returns>
        public static string ByteArrayToHexString(this byte[] target, string separator = " ")
        {
            return BitConverter.ToString(target).Replace("-", separator);
        }

        /// <summary>
        /// 현재 문자열이 숫자인지 여부를 나타냅니다.
        /// </summary>
        /// <param name="target">호출한 대상 인스턴스입니다.</param>
        /// <returns>문자열이 숫자이면 true이고 그렇지 않으면 false입니다.</returns>
        public static bool IsNumeric(this string target)
        {
            int result;
            return Int32.TryParse(target, out result);
        }

        #endregion Extend Method
    }
}